package com.xuecheng.search.service;

import com.xuecheng.framework.domain.course.CoursePub;
import com.xuecheng.framework.domain.course.response.TeachplanMediaPub;
import com.xuecheng.framework.domain.search.CourseSearchParam;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * es搜索服务
 *
 * @author NN
 */
@Service
public class EsCourseService {

    @Value("${xuecheng.course.index}")
    private String index;
    @Value("${xuecheng.course.type}")
    private String type;
    @Value("${xuecheng.course.source_field}")
    private String source_field;

    @Value("${xuecheng.media.index}")
    private String media_index;
    @Value("${xuecheng.media.type}")
    private String media_type;
    @Value("${xuecheng.media.source_field}")
    private String media_source_field;

    @Autowired
    RestHighLevelClient restHighLevelClient;


    /**
     * 课程搜索
     *
     * @param page              页码
     * @param size              每页显示条数
     * @param courseSearchParam 搜索参数
     * @return 查询结果
     */
    public QueryResponseResult list(int page, int size, CourseSearchParam courseSearchParam) throws IOException {
        if (courseSearchParam == null) {
            courseSearchParam = new CourseSearchParam();
        }
        //1. 创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest(index);
            // 设置搜索类型
        searchRequest.types(type);
        //2. 创建 搜索源构建 对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            // 指定过滤源字段
        searchSourceBuilder.fetchSource(source_field.split(","), new String[]{});
            // 设置分页参数
        if (page <= 0) {
            page = 1;
        }
        if (size <= 0) {
            size = 12;
        }
        int startIndex = (page - 1) * size;
        searchSourceBuilder.from(startIndex);
        searchSourceBuilder.size(size);
            //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font class='eslight'>");
        highlightBuilder.postTags("</font>");
            //高亮字段
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
        searchSourceBuilder.highlighter(highlightBuilder);
        // 3. 创建bool查询对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            // 搜索条件
        String keyword = courseSearchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "description", "teachplan")
                    .minimumShouldMatch("70%").field("name", 10);
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }
            // 过滤
            // 一级分类
        String mt = courseSearchParam.getMt();
        if (!StringUtils.isEmpty(mt)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt", mt));
        }
            // 二级分类
        String st = courseSearchParam.getSt();
        if (!StringUtils.isEmpty(st)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("st", st));
        }
            // 难度等级
        String grade = courseSearchParam.getGrade();
        if (!StringUtils.isEmpty(grade)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", grade));
        }
        //4. 将<bool查询对象>设置到<搜索源对象>中
        searchSourceBuilder.query(boolQueryBuilder);
        //5. 将<搜索源对象>设置到<搜索请求对象>中
        searchRequest.source(searchSourceBuilder);
        //6. 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
            // 处理查询结果
        SearchHits hits = searchResponse.getHits();
            // 总记录数
        long totalHits = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();
            // 定义集合封装查询结果
        ArrayList<CoursePub> coursePubArrayList = new ArrayList<CoursePub>();
        for (SearchHit hit : searchHits) {
            CoursePub coursePub = new CoursePub();
            //获取索引库的源文档
            Map<String, Object> sourceDoc = hit.getSourceAsMap();

            // 封装 name
            String name = (String) sourceDoc.get("name");
            // id
            String id = (String) sourceDoc.get("id");
            coursePub.setId(id);
            //取出高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields.get("name") != null) {
                HighlightField highlightField = highlightFields.get("name");
                Text[] fragments = highlightField.fragments();
                StringBuffer stringBuffer = new StringBuffer();
                for (Text text : fragments) {
                    stringBuffer.append(text);
                }
                name = stringBuffer.toString();
            }
            coursePub.setName(name);
                // 图片
            String pic = (String) sourceDoc.get("pic");
            coursePub.setPic(pic);
                // 价格
            Object price = sourceDoc.get("price");
            if (!StringUtils.isEmpty(price)) {
                //价格有效
                float priceFloat = Float.parseFloat(String.valueOf(price));
                coursePub.setPrice(priceFloat);
            }
                // 旧价格
            Object price_old = sourceDoc.get("price_old");
            if (!StringUtils.isEmpty(price_old)) {
                float priceFloat_old = Float.parseFloat(String.valueOf(price_old));
                coursePub.setPrice_old(priceFloat_old);
            }
                // 放入集合中
            coursePubArrayList.add(coursePub);
        }
        //7. 响应前端
        QueryResult<CoursePub> queryResult = new QueryResult<CoursePub>();
        queryResult.setTotal(totalHits);
        queryResult.setList(coursePubArrayList);
        return new QueryResponseResult(CommonCode.SUCCESS, queryResult);
    }


    /**
     * 根据课程id查询课程信息, 用于视频播放页面查询课程计划, 根据课程计划播放相应视频
     * @param courseId 课程id
     * @return
     */
    public Map<String, CoursePub> getAll(String courseId) {
        //1. 创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest(index);
            // 设置搜索类型
        searchRequest.types(type);
        //2. 创建 搜索源构建 对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //3. 创建搜索对象
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("id", courseId);
        //4. 进行搜索
        searchSourceBuilder.query(termQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //5. 获取搜索结果
        SearchHits hits = searchResponse.getHits();
        SearchHit[] hitsArray = hits.getHits();
            //创建coursePub对象, 封装课程所有信息并相应前端
        CoursePub coursePub = new CoursePub();
            //响应前端的map
        Map<String, CoursePub> responseMap = new HashMap<String, CoursePub>();
        for (SearchHit searchHit : hitsArray) {
            Map<String, Object> sourceMap = searchHit.getSourceAsMap();
            String id = (String) sourceMap.get("id");
            String name = (String) sourceMap.get("name");
            String grade = (String) sourceMap.get("grade");
            String charge = (String) sourceMap.get("charge");
            String pic = (String) sourceMap.get("pic");
            String description = (String) sourceMap.get("description");
            String teachplan = (String) sourceMap.get("teachplan");
            //设置courepub属性
            coursePub.setId(id);
            coursePub.setName(name);
            coursePub.setGrade(grade);
            coursePub.setCharge(charge);
            coursePub.setPic(pic);
            coursePub.setDescription(description);
            coursePub.setTeachplan(teachplan);
            //放入map中
            responseMap.put(id, coursePub);
        }
        return responseMap;
    }

    /**
     * 根据课程计划id数组查询对应的媒资信息, 用于视频播放页面
     * @param teachplanIds 课程计划id数组
     * @return 课程计划对应的媒资信息
     */
    public QueryResponseResult getMediaByTeachplanIds(String[] teachplanIds) {
        //1. 创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest(media_index);
            // 设置搜索类型
        searchRequest.types(media_type);
        //2. 创建搜索源对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //设置使用termsQuery查询, 可以传入多个id
        searchSourceBuilder.query(QueryBuilders.termsQuery("teachplan_id", teachplanIds));
            //过滤源字段
        String[] split = media_source_field.split(",");
        searchSourceBuilder.fetchSource(split, new String[]{});
        //3. 将搜索源对象放入搜索请求对象中
        searchRequest.source(searchSourceBuilder);
        //4. 通过客户端进行查询
        SearchResponse searchResponse = null;
        long total = 0;
        try {
            searchResponse = restHighLevelClient.search(searchRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SearchHits hits = searchResponse.getHits();
        //总匹配数
        total = hits.getTotalHits();
        SearchHit[] hitsArr = hits.getHits();
        // 5. 封装结果
        List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<TeachplanMediaPub>();
        for (SearchHit hit : hitsArr) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String courseid = (String) sourceAsMap.get("courseid");
            String media_fileoriginalname = (String) sourceAsMap.get("media_fileoriginalname");
            String media_id = (String) sourceAsMap.get("media_id");
            String media_url = (String) sourceAsMap.get("media_url");
            String teachplan_id = (String) sourceAsMap.get("teachplan_id");
            //实体对象
            TeachplanMediaPub teachplanMediaPub = new TeachplanMediaPub();
            teachplanMediaPub.setCourseId(courseid);
            teachplanMediaPub.setMediaFileOriginalName(media_fileoriginalname);
            teachplanMediaPub.setMediaId(media_id);
            teachplanMediaPub.setMediaUrl(media_url);
            teachplanMediaPub.setTeachplanId(teachplan_id);
            //将提示对象放入集合
            teachplanMediaPubList.add(teachplanMediaPub);
        }
        // 6. 响应
        QueryResult<TeachplanMediaPub> queryResult = new QueryResult<TeachplanMediaPub>();
        queryResult.setList(teachplanMediaPubList);
        queryResult.setTotal(total);
        return new QueryResponseResult(CommonCode.SUCCESS, queryResult);
    }
}
